# Video Library - Interface for Entertainer video library cache
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import os
from pysqlite2 import dbapi2 as sqlite
from frontend.medialibrary.playable import Playable

################################################################################
# This file contains five classes that together consists video library 
# interface. These classes are VideoLibrary, VideoClip, Movie, TVEpisode and 
# TVSeries.
################################################################################

class VideoLibrary:
    """
    Video library. 
    
    Interface for Entertainer's video cache.
    """
    
    # Video cache database
    VIDEO_DB = os.path.expanduser('~/.entertainer/cache/video.db')
    
    def __init__(self, backend_connection):
        """
        Initialize video library
        @param backend_connection: BackendConnection object
        """
        if not os.path.exists(self.__class__.VIDEO_DB):
            raise Exception("Video database doesn't exists!")
        self.backend_connection = backend_connection
    
    def get_movies(self):
        """
        Get all movies
        @return: List of Movie objects
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT videofile.filename
                          FROM   videofile, metadata
                          WHERE  type='MOVIE'
                          AND    videofile.filename=metadata.filename
                          ORDER BY title""")
        movies = []
        for row in cursor:
            movies.append(Movie(row[0]))
        connection.close()
        return movies

    def get_tv_series(self):
        """
        Get all TV-series.
        @return: List of TV-series titles (strings)
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT DISTINCT series_title
                          FROM   metadata
                          WHERE  type='TV-SERIES'
                          ORDER BY title""")
        series = []
        for row in cursor:
            series.append(TVSeries(row[0]))
        connection.close()
        return series
        
    def get_video_clips(self):
        """
        Get List of videos that or not movies or tv-series episodes.
        @return: List of VideoClip objects
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT filename
                          FROM   metadata
                          WHERE  type='CLIP'
                          ORDER BY title""")
        clips = []
        for row in cursor:
            clips.append(VideoClip(row[0]))
        connection.close()
        return clips
        
    def get_number_of_movies(self):
        """
        Get number of movies in the video library
        @return: Integer
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT COUNT(filename)
                          FROM   metadata
                          WHERE  type='MOVIE'""")
        result = cursor.fetchall()
        connection.close()
        return result[0][0]
        
    def get_number_of_tv_series(self):
        """
        Get number of TV-series in the video library
        @return: Integer
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT DISTINCT COUNT(series_title)
                          FROM   metadata
                          WHERE  type='TV-SERIES'""")
        result = cursor.fetchall()
        connection.close()
        return result[0][0]
        
    def get_number_of_video_clips(self):
        """
        Get number of misc. VideoClips in the video library
        @return: Integer
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT COUNT(filename)
                          FROM   metadata
                          WHERE  type='CLIP'""")
        result = cursor.fetchall()
        connection.close()
        return result[0][0]
        
################################################################################

class VideoClip(Playable):
    """
    Objects from this class represents video files.
    """
    
    # Video cache database
    VIDEO_DB = os.path.expanduser('~/.entertainer/cache/video.db')
    
    def __init__(self, filename):
        """
        Initialize Clip information
        @param filename: Absolute path of the video clip file
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT videofile.filename, hash, length, resolution, title
                          FROM   videofile, metadata
                          WHERE  videofile.filename=:fn
                          AND    videofile.filename=metadata.filename
                          ORDER BY title""", { "fn" : filename })
        result = cursor.fetchall()
        self.__filename = result[0][0]    # Filename of the clip
        self.__art_hash = result[0][1]    # Cover art hash
        self.__length = result[0][2]      # Lenght of the movie
        self.__resolution = result[0][3]  # Video resolution
        self.__title = result[0][4]       # Clip title
        connection.close()

    def get_filename(self):
        """
        Get filename (abs path) of the clip
        @return: String
        """
        return self.__filename
        
    def get_title(self):
        """
        Get title of the clip
        @return: String
        """
        return self.__title
        
    def get_length(self):
        """
        Get length of the clip
        @return: Integer
        """
        return self.__length
        
    def get_resolution(self):
        """
        Get resolution of the clip
        @return: String
        """
        return self.__resolution
        
    def get_thumbnail_url(self):
        """
        Get absolute path of the thumbnail image file.
        @return: String
        """
        thumb_path = os.path.expanduser('~/.entertainer/cache/thumbnails/video')
        thumb = os.path.join(thumb_path, self.__art_hash + ".jpg")
        if os.path.exists(thumb):
            return thumb
        else:
            return None
    
    # Implement playable interface
    def get_type(self):
        """
        Track is an audio stream.
        @return: Integer
        """
        return Playable.VIDEO_STREAM
    
    def get_uri(self):
        """
        Get URI of the audio file
        @return: String
        """
        return "file://" + self.__filename
            
################################################################################

class Movie(Playable):
    """
    Objects from this class represents movie.
    """
    
    # Cover art folder
    ART_DIRECTORY = os.path.expanduser('~/.entertainer/cache/movie_art')
    
    # Image cache database
    VIDEO_DB = os.path.expanduser('~/.entertainer/cache/video.db')
    
    def __init__(self, filename):
        """
        Initialize Movie information
        @param filename: Absolute path of the Movie file
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT metadata.filename, hash, length, resolution,
                                 title, runtime, genres, rating, year,
                                 plot_outline, plot, actor_1, actor_2,
                                 actor_3, actor_4, actor_5, writer_1,
                                 writer_2, director_1, director_2
                          FROM   videofile, metadata
                          WHERE  metadata.filename=:fn
                          AND    videofile.filename=metadata.filename
                          ORDER BY title""", { "fn" : filename })
        result = cursor.fetchall()
        self.__filename = result[0][0]
        self.__art_hash = result[0][1]
        self.__length = result[0][2]
        self.__resolution = result[0][3]
        self.__title = result[0][4]
        self.__runtime = result[0][5]
        self.__genres = result[0][6].split(',')
        self.__rating = result[0][7]
        self.__year = result[0][8]
        self.__plot_outline = result[0][9]
        self.__plot = result[0][10]
        self.__actors = []
        self.__writers = []
        self.__directors = []
        
        # Actors
        if len(result[0][11]) > 0:
            self.__actors.append(result[0][11])
        if len(result[0][12]) > 0:
            self.__actors.append(result[0][12])
        if len(result[0][13]) > 0:
            self.__actors.append(result[0][13])
        if len(result[0][14]) > 0:
            self.__actors.append(result[0][14])
        if len(result[0][15]) > 0:
            self.__actors.append(result[0][15])
        
        # Writers
        if len(result[0][16]) > 0:
            self.__writers.append(result[0][16])
        if len(result[0][17]) > 0:
            self.__writers.append(result[0][17])
        
        # Directors
        if len(result[0][18]) > 0:
            self.__directors.append(result[0][18])
        if len(result[0][19]) > 0:
            self.__directors.append(result[0][19])
            
        connection.close()

    def get_filename(self):
        """
        Get filename (abs path) of the movie
        @return: String 
        """
        return self.__filename
        
    def get_title(self):
        """
        Get title of the movie
        @return: String 
        """
        return self.__title
    
    def has_cover_art(self):
        """
        Is there cover art for this movie?
        @return: Boolean
        """
        movie_art = os.path.join(self.__class__.ART_DIRECTORY, self.__title + ".jpg")
        return os.path.exists(movie_art)
    
    def get_cover_art_url(self):
        """
        Get absolute filename of the cover art image file.
        @return: String 
        """
        return os.path.join(self.__class__.ART_DIRECTORY, self.__title + ".jpg")
        
    def get_length(self):
        """
        Get length of the movie
        @return: String
        """
        return self.__length
        
    def get_resolution(self):
        """
        Get resolution of the movie
        @return: String
        """
        return self.__resolution

    def get_runtime(self):
        """Return runtime of the movie"""
        return self.__runtime
        
    def get_genres(self):
        """Return list of genre strings"""
        return self.__genres
        
    def get_rating(self):
        """Return rating of the movie (1-5)"""
        return self.__rating

    def get_year(self):
        """Return release year of the movie"""
        return self.__year
        
    def get_short_plot(self):
        """Return plot outline text."""
        return self.__plot_outline
        
    def get_plot(self):
        """Return long plot text."""
        return self.__plot
        
    def get_actors(self):
        """Return list of actors"""
        return self.__actors
        
    def get_writers(self):
        """Return list of writers"""
        return self.__writers
        
    def get_directors(self):
        """Return list of directors"""
        return self.__directors
        
    def get_thumbnail_url(self):
        """Return absolute path of the thumbnail image file."""
        thumb_path = os.path.expanduser('~/.entertainer/cache/thumbnails/video')
        thumb = os.path.join(thumb_path, self.__art_hash + ".jpg")
        if os.path.exists(thumb):
            return thumb
        else:
            return None

    # Implement playable interface
    def get_type(self):
        """
        Track is an audio stream.
        @return: Integer
        """
        return Playable.VIDEO_STREAM
    
    def get_uri(self):
        """
        Get URI of the audio file
        @return: String
        """
        return "file://" + self.__filename
    
    def remove(self):
        """Remove this movie from cache and harddrive."""
        print "Remove not implemented: " + self.__filename

################################################################################

class TVSeries(object):
    """
    TV Series
    
    TV Series contains TVEpisodes organized by seasons.
    """
    
    # Image cache database
    VIDEO_DB = os.path.expanduser('~/.entertainer/cache/video.db')
    
    # Cover art folder
    ART_DIRECTORY = os.path.expanduser('~/.entertainer/cache/movie_art')
    
    def __init__(self, title):
        """
        Initialize TV-Series
        @param title: Series title
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT DISTINCT series_title
                          FROM   metadata
                          WHERE  series_title=:title""", { "title" : title })
        result = cursor.fetchall()
        if len(result) == 0:
            raise Exception("No such a TV-series as " + title)
        else:
            self.__title = title
        connection.close()

    def get_title(self):
        """
        Get title of the series
        @return: String
        """
        return self.__title
    
    def has_cover_art(self):
        """
        Is there cover art file for this TV-serie
        @return: String
        """
        art_path =  os.path.join(self.__class__.ART_DIRECTORY, self.__title + ".jpg")
        return os.path.exists(art_path)
    
    def get_cover_art_url(self):
        """
        Get absolute filename of the cover art image file.
        @return: String
        """
        return os.path.join(self.__class__.ART_DIRECTORY, self.__title + ".jpg")
        
    def get_seasons(self):
        """
        Return list of numbers. For example [1,2,3,5] in case that user
        doesn't have episodes from season 4.
        @return: List of Integers
        """
        seasons = []
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT DISTINCT season
                          FROM   metadata
                          WHERE  series_title=:title""", 
                          { "title" : self.__title })
        for row in cursor:
            seasons.append(row[0])
        connection.close()
        return seasons
        
    def get_number_of_episodes(self):
        """
        Get number of episodes available on this series
        @return: Integer
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT COUNT(filename)
                          FROM   metadata
                          WHERE  series_title=:title""", 
                          { "title" : self.__title })
        result = cursor.fetchall()
        connection.close()
        return result[0][0]
        
    def get_number_of_seasons(self):
        """
        Get number of seasons available on this series
        @return: Integer
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT COUNT(DISTINCT season)
                          FROM   metadata
                          WHERE  series_title=:title""", 
                          { "title" : self.__title })
        result = cursor.fetchall()
        connection.close()
        return result[0][0]
        
    def get_episodes_from_season(self, season):
        """
        Get all episodes of the season.
        @param season: Season number (Integer)
        """
        episodes = []
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT filename, episode
                          FROM   metadata
                          WHERE  series_title=:title
                          AND    season=:season
                          ORDER BY episode""", 
                          { "title" : self.__title, "season" : season })
        for row in cursor:
            episodes.append(TVEpisode(row[0], self))
        connection.close()
        return episodes
        
    def get_episode_from_season(self, season, episode):
        """
        Get specific episode from this series.
        @param season: Season number (Integer)
        @param episode: Episode number (Integer)
        @return TVEpisode object
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT filename
                          FROM   metadata
                          WHERE  series_title=:title
                          AND    episode=:episode
                          AND    season=:season""", 
                          { "title" : self.__title, 
                            "season" : season,
                            "episode" : episode })
        result = cursor.fetchall()
        episode = TVEpisode(result[0][0], self)
        connection.close()
        return episode
        
    def get_episodes(self):
        """
        Get all episodes of this series.
        @return: List of TVEpisode objects
        """
        episodes = []
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT filename, season, episode
                          FROM   metadata
                          WHERE  series_title=:title
                          ORDER BY season, episode""", 
                          { "title" : self.__title })
        for row in cursor:
            episodes.append(TVEpisode(row[0], self))
        connection.close()
        return episodes

################################################################################

class TVEpisode(Playable):
    """
    Objects from this class represents TV-Episode video file.
    """
    
    # Image cache database
    VIDEO_DB = os.path.expanduser('~/.entertainer/cache/video.db')
    
    # Cover art folder
    ART_DIRECTORY = os.path.expanduser('~/.entertainer/cache/movie_art')
    
    def __init__(self, filename, series):
        """
        Initialize episode information.
        @param filename: Name of the video file
        @param series: TVSeries object
        """
        connection = sqlite.connect(self.__class__.VIDEO_DB)
        cursor = connection.cursor()
        cursor.execute("""SELECT videofile.filename, hash, length, resolution,
                                 title, runtime, genres, rating, year,
                                 plot_outline, plot, actor_1, actor_2,
                                 actor_3, actor_4, actor_5, writer_1,
                                 writer_2, director_1, director_2, 
                                 series_title, season, episode
                          FROM   videofile, metadata
                          WHERE  videofile.filename=:fn
                          AND    videofile.filename=metadata.filename
                          ORDER BY title""", { "fn" : filename })
        result = cursor.fetchall()
        self.filename = result[0][0]
        self.art_hash = result[0][1]
        self.length = result[0][2]
        self.resolution = result[0][3]
        self.title = result[0][4]
        self.runtime = result[0][5]
        self.genres = result[0][6].split(',')
        self.rating = result[0][7]
        self.year = result[0][8]
        self.plot_outline = result[0][9]
        self.plot = result[0][10]
        
        # Actors
        self.actors = []
        if len(result[0][11]) > 0:
            self.actors.append(result[0][11])
        if len(result[0][12]) > 0:
            self.actors.append(result[0][12])
        if len(result[0][13]) > 0:
            self.actors.append(result[0][13])
        if len(result[0][14]) > 0:
            self.actors.append(result[0][14])
        if len(result[0][15]) > 0:
            self.actors.append(result[0][15])
        
        # Writers
        self.writers = []
        if len(result[0][16]) > 0:
            self.writers.append(result[0][16])
        if len(result[0][17]) > 0:
            self.writers.append(result[0][17])
        
        # Directors
        self.directors = []
        if len(result[0][18]) > 0:
            self.directors.append(result[0][18])
        if len(result[0][19]) > 0:
            self.directors.append(result[0][19])
            
        self.series = series
        self.season = result[0][21]
        self.episode_number = result[0][22]
        connection.close()

    def get_filename(self):
        """
        Get filename (abs path) of the episode.
        @return: String
        """
        return self.filename
        
    def get_title(self):
        """
        Get title of the episode
        @return: String
        """
        return self.title
        
    def get_cover_art_url(self):
        """
        Get absolute filename of the cover art image file.
        @return: String
        """
        return os.path.join(self.__class__.ART_DIRECTORY, 
                            self.series_title + ".jpg")
        
    def get_length(self):
        """
        Get length of the episode
        @return: String
        """
        return self.length
        
    def get_resolution(self):
        """
        Get resolution of the episode
        @return: String 
        """
        return self.resolution

    def get_runtime(self):
        """
        Get runtime of the episode
        @return: String
        """
        return self.runtime
        
    def get_genres(self):
        """
        Get list of genre strings
        @return: List of strings
        """
        return self.genres
        
    def get_rating(self):
        """
        Get rating of the episode (1-5)
        @return: Integer
        """
        return int(self.rating)

    def get_year(self):
        """
        Get release year of the episode.
        @return: Interger
        """
        return int(self.year)
        
    def get_short_plot(self):
        """
        Get plot outline text.
        @return: String
        """
        return self.plot_outline
        
    def get_plot(self):
        """
        Get long plot text.
        @return: String
        """
        return self.plot
        
    def get_actors(self):
        """
        Get list of actors
        @return: List of strings
        """
        return self.actors
        
    def get_writers(self):
        """
        Get list of writers
        @return: List of strings
        """
        return self.writers
        
    def get_directors(self):
        """
        Get list of directors
        @return: List of strings
        """
        return self.directors
        
    def get_series(self):
        """
        Get TV-series
        @return: TVSeries objects
        """
        return self.series
        
    def get_episode_number(self):
        """
        Get number of the episode
        @return: Integer
        """
        return int(self.episode_number)
        
    def get_season(self):
        """
        Get number of the season
        @return: Interger
        """
        return int(self.season)
        
    def get_thumbnail_url(self):
        """
        Get absolute path of the thumbnail image file.
        @return: String
        """
        if self.art_hash is not None:
            thumb_path = os.path.expanduser('~/.entertainer/cache/thumbnails/video')
            thumb = os.path.join(thumb_path, self.art_hash + ".jpg")
            if os.path.exists(thumb):
                return thumb
            else:
                return None
        else:
            return None
            
    # Implement playable interface
    def get_type(self):
        """
        Track is an audio stream.
        @return: Integer
        """
        return Playable.VIDEO_STREAM
    
    def get_uri(self):
        """
        Get URI of the audio file
        @return: String
        """
        return "file://" + self.filename
